#include <cstdio>
#include <stdint.h>
#include <unistd.h>
#include <cstring>
#include "packet.h"
#include "device_info.h"
#include "group_command.h"
#include "fifo.h"

uint8_t ACKTxLayer_status = 0;
uint8_t AdjacentID = 0x00;
uint8_t MY_ID = 0x20;
uint8_t G_ID = 0x01;
extern int serial_fd;
extern bool send_idle;
uint8_t last_local_id = 0;
uint8_t last_remote_id = 0;

uint8_t last_command[64] = {0};
bool send_command_flag = true;
extern int default_time_ms;
extern int start_timer(int fd, int ms, bool expired);
int start_timer_flags = false;
int loop_time_flag = false;

int adjcent_link = 0;
extern int internal_fifo_fd;
int update_last_command(uint8_t *dest, uint8_t *src, int size);
void delay_ms(int ms)
{

    usleep(1000 * ms);
}
#if 0
int report3_message()
{

    uint8_t data[64] = {0x01, 0x07, 0x20, 0x31, 0x42, 0x03, 0x00, 0x00, 0x22, 0x40};
    uint8_t len = data[1] + 3;
    write(serial_fd, data, len);
    output_data("send data", data, len);
    update_last_command(last_command, data, len);
    last_local_id = 0x20;
    last_remote_id = 0x01;
    return 0;
}
int report2_message()
{

    uint8_t data[64] = {0x01, 0x2e, 0x20, 0x31, 0x02, 0x2a, 0xca, 0x00, 0x22, 0xc9, 0x00, 0x21, 0xc7, 0xff, 0x02, 0xc6, 0xfe, 0x00, 0xac, 0xd8, 0x60, 0xaa, 0x05, 0x7e, 0xa9, 0xef, 0x02, 0xa2, 0xff, 0xc6, 0x9f, 0xff, 0x00, 0x9b, 0x15, 0xdb, 0x10, 0xf7, 0x80, 0x0b, 0xe2, 0xfb, 0x0a, 0xdc, 0x92, 0x09, 0xd8, 0x00, 0xef};
    uint8_t len = data[1] + 3;
    write(serial_fd, data, len);
    output_data("send data", data, len);
    update_last_command(last_command, data, len);
    last_local_id = 0x20;
    last_remote_id = 0x01;
    return 0;
}
int report1_message()
{
    uint8_t data[64] = {0x01, 0x10, 0x20, 0x30, 0x02, 0x0c, 0x87, 0x03, 0x84, 0x08, 0x83, 0xc0, 0x82, 0x25, 0x81, 0x39, 0x06, 0x08, 0xc9};
    uint8_t len = data[1] + 3;
    write(serial_fd, data, len);
    last_local_id = 0x20;
    last_remote_id = 0x01;
    output_data("send data", data, len);
    update_last_command(last_command, data, len);

    return 0;
}
#endif
int send_who_are_you_request()
{
    uint8_t command[64] = {0};
    uint8_t paylen = 0x02;
    command[0] = 0x00;
    command[1] = paylen;
    command[2] = MY_ID;
    command[3] = WHO_ARE_YOU;
    command[4] = 0xc1;
    write(serial_fd, command, paylen + 3);
    update_last_command(last_command, command, paylen + 3);

    last_local_id = MY_ID;
    last_remote_id = 0x00;
}

int sendack(uint8_t remoteid, uint8_t localid)
{
    uint8_t data[64] = {0};
    uint8_t totalsum = 0;
    data[0] = remoteid;
    data[1] = 0x02;
    data[2] = localid;
    data[3] = ACK;
    for (int i = 0; i < 4; i++)
    {
        totalsum += data[i];
    }
    data[4] = 0 - totalsum;
    write(serial_fd, data, 5);
    output_data("response ack", data, 5);
    return 0;
}
int update_ack_status(uint8_t status)
{
    ACKTxLayer_status = status;
    return 0;
}
int update_last_command(uint8_t *dest, uint8_t *src, int size)
{
    memcpy(dest, src, size);
    return 0;
}
int send_nak(uint8_t remoteid, uint8_t localid)
{
    uint8_t data[64] = {0};
    uint8_t totalsum = 0;
    data[0] = remoteid;
    data[1] = 0x02;
    data[2] = localid;
    data[3] = NAK;
    for (int i = 0; i < 4; i++)
    {
        totalsum += data[i];
    }
    data[4] = 0 - totalsum;
    write(serial_fd, data, 5);
    output_data("response nak", data, 5);
    return 0;
}
int send_who_are_you_command(uint8_t remoteid, uint8_t localid)
{

    uint8_t data[64] = {0};
    uint8_t totalsum = 0;
    data[0] = remoteid;
    data[1] = 0x02;
    data[2] = localid;
    data[3] = WHO_ARE_YOU;
    for (int i = 0; i < 4; i++)
    {
        totalsum += data[i];
    }
    data[4] = 0 - totalsum;
    write(serial_fd, data, 5);
    update_last_command(last_command, data, 5);
    output_data("send who are you", data, 5);
    last_local_id = localid;
    last_remote_id = remoteid;

    return 0;
}
int retranmit_last_command(uint8_t remoteid, uint8_t localid)
{
    int len = last_command[1] + 3;
    if (len > 64)
    {
        printf("last command data is error!\n");
        return -1;
    }
    write(serial_fd, last_command, len);
    output_data("retranmit_last_command", last_command, len);
}

int handle_who_are_you(uint8_t remoteid, uint8_t localid)
{
    uint8_t buffer[64] = {0};
    buffer[0] = WHO_ARE_YOU;
    buffer[1] = remoteid;
    buffer[2] = localid;

    notify_message(internal_fifo_fd,buffer, 3);

    return 0;
}
int send_arm_here(uint8_t remoteid, uint8_t localid)
{

    uint8_t buffer[64] = {0};
    buffer[0] = I_AM_HERE;
    buffer[1] = remoteid;
    buffer[2] = localid;
    notify_message(internal_fifo_fd,buffer, 3);
    return 0;
}
int handle_information(uint8_t type)
{

    uint8_t buffer[64] = {0};
    buffer[0] = type;
    notify_message(internal_fifo_fd,buffer, 1);

    return 0;
}
int handle_auto_response()
{
    uint8_t buffer[64] = {0};
    buffer[0] = ACK;
    notify_message(internal_fifo_fd,buffer, 1);

    return 0;
}
int init_send = 3;
int handle_general_purpose_commands(uint8_t remoteid, uint8_t localid,
                                    uint8_t *data, int llen)
{
    uint8_t group_command = data[0];
    int comamd_len = 0;
    switch (group_command)
    {
    case ACK:
        send_command_flag = true;
        comamd_len = 1;
        if (AdjacentID)
        {
            if (adjcent_link == 0)
            {
                adjcent_link = 1;
            }
        }
        if (last_remote_id == remoteid && localid == last_local_id)
        {
            send_idle = true;
        }
        break;
    case NAK:
        retranmit_last_command(remoteid, MY_ID);
        comamd_len = 1;
        break;
    case WAIT:
        comamd_len = 1;
        sendack(remoteid, localid);
        break;
    case ACTIVE_SENDSING:
        comamd_len = 1;
        sendack(remoteid, localid);
        break;
    case LINK:
        comamd_len = data[2];
        sendack(remoteid, localid);
        break;
    case MATRIX_SELECT:
        comamd_len = data[2];
        sendack(remoteid, localid);
        break;
    case SYSTEM_SWITCH_REL:
        comamd_len = 3;
        sendack(remoteid, localid);
        break;
    case SYSTEM_SWITCH_ABS:
        comamd_len = 3;
        sendack(remoteid, localid);
        break;
    case RECOVERY:
        comamd_len = 2;
        sendack(remoteid, localid);
        break;
    case SWITCH_WITH_SENDER:
        comamd_len = 4;
        sendack(remoteid, localid);
        break;
    case SYSTEM_STATUS_REQUEST_TRANSMIT:
        comamd_len = data[2];
        sendack(remoteid, localid);
        break;
    case CHARACTOR_PATTERN_REQUEST_TRANSMIT:
        comamd_len = data[2];
        sendack(remoteid, localid);
        break;
    case FIL_TRANSFER:
        comamd_len = data[2];
        sendack(remoteid, localid);
        break;
    case INFORMATION:
        comamd_len = data[2];
        sendack(remoteid, localid);
        handle_information(INFORMATION);
        break;
    case OTHER_SYSTEM_COMMAND_DATA_TRANSFER:
        comamd_len = data[2];
        sendack(remoteid, localid);
        break;
    case PROTOCOL_LEVEL:
        comamd_len = 3;
        sendack(remoteid, localid);
        break;
    case 0x1b:
        adjcent_link = 0;
        AdjacentID = 0x00;
        send_who_are_you_command(AdjacentID, MY_ID);
        comamd_len = 61;
        break;
    case NOP:
        comamd_len = 1;
        break;
    case WHO_ARE_YOU:
        /*
        该命令用于在主电源接通、线缆接通或其他情况下启动通信。使用实例在主电源接通、线缆接通等情况下，
        使用设置为DEST_ID的G_ID下发此命令。当设备接收到此命令时，
        将相邻发送设备的ID设置为DEST_ID，将自己的ID设置为SRC_ID，并返回此命令
        */
        comamd_len = 2;
        if (remoteid && localid)
            AdjacentID = remoteid;
        sendack(remoteid, localid);
        if (adjcent_link == 0)
        {
            handle_who_are_you(localid == 0x00 ? 0x00 : remoteid, 0x20);
        }
        else
        {
            handle_who_are_you(0x90, 0x20);
        }
        break;
    case I_AM_HERE:
        comamd_len = 1;
        sendack(remoteid, localid);
        break;
    case ARE_YOU_THERE:
        sendack(remoteid, localid);
        comamd_len = 1;
        send_arm_here(remoteid,localid);
        break;
    default:
        printf("--------------------type:%02x\n", group_command);
        sendack(remoteid, localid);

        comamd_len = 64;
        break;
    }

    return comamd_len;
}
int handle_switch_rel(uint8_t *cmd_body, int len)
{
    uint8_t buff[64], nlen;
    for (int i = 0; i < len; i++)
    {
        buff[i] = cmd_body[i];
    }

    nlen = notify_message(internal_fifo_fd,buff, len);
    return nlen;
}
int handle_analog_rel(uint8_t type, uint8_t *cmd_body, int len, int per_command_size)
{

    uint8_t buff[64], nlen, j = 0;
    output_data("cmd_body data:", cmd_body, len);
    for (int i = 0; i < len; i++)
    {
        buff[i] = cmd_body[i];
    }
    nlen = notify_message(internal_fifo_fd,buff, len);
    return nlen;
}
int handle_ext_analog_rel(uint8_t type, uint8_t *cmd_body, int len, int per_command_size)
{
    uint8_t buff[64], nlen, j = 0;
    uint8_t *ptr = cmd_body;
    buff[j++] = type;
    output_data("cmd_body data:", cmd_body, len);

    for (int i = 0; i < len; i = i + per_command_size)
    {
        buff[j++] = cmd_body[i + 1];
    }
    nlen = notify_message(internal_fifo_fd,buff, len / per_command_size + 1);
    return nlen;
}
int handle_auto_setup(uint8_t type)
{
    uint8_t buff[64], nlen, j = 0;
    buff[0] = type;
    nlen = notify_message(internal_fifo_fd,buff, 1);
    return nlen;
}
int handle_setup_info(uint8_t type)
{

    uint8_t buff[64], nlen, j = 0;
    buff[0] = type;
    nlen = notify_message(internal_fifo_fd,buff, 1);
    return nlen;

    return 0;
}
int handle_chu_switch_abs(uint8_t *cmd_body, int len)
{
    uint8_t buff[64] = {0}, nlen, j = 0;
    for (int i = 0; i < len; i++)
    {
        buff[i] = cmd_body[i];
    }
    nlen = notify_message(internal_fifo_fd,buff, len);
    return nlen;
}
int handle_command_for_chu(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    uint8_t comamd_len = 1;
    uint8_t type = data[0];
    switch (type)
    {
    case CHU_SWITCH_REL:
        comamd_len = 3;
        sendack(remoteid, localid);
        handle_switch_rel(data, llen);
        break;
    case CHU_SWITCH_ABS:
        sendack(remoteid, localid);
        handle_chu_switch_abs(data, llen);
        break;
    case CHU_ANALOG_REL:
        comamd_len = 4;
        sendack(remoteid, localid);
        handle_analog_rel(type, data, llen, comamd_len);
        break;
    case CHU_ANALOG_ABS:
        comamd_len = 3;
        sendack(remoteid, localid);
        break;
    case CHU_DIAGNOSIS_INFORMATION:
    case CHU_AUTO_SETUP_CONTROL:
        sendack(remoteid, localid);
        handle_auto_setup(CHU_AUTO_SETUP_CONTROL);
        break;
    case CHU_AUTO_SETUP_INFORMATION:
        sendack(remoteid, localid);
        handle_setup_info(CHU_AUTO_SETUP_INFORMATION);
        break;
    case CHU_FILE_CONTROL:
        sendack(remoteid, localid);
        break;
    case CHU_FILE_NAME_CONTROL:
        sendack(remoteid, localid);
        break;
    case CHU_SWITCH_WITH_MASK:
        sendack(remoteid, localid);
        break;
    case CHU_ANALOG_FOR_MASTER_SLAVE:
        sendack(remoteid, localid);
        break;
    case CHU_EXT_ANALOG_REL:
        comamd_len = 4;
        sendack(remoteid, localid);
        handle_ext_analog_rel(type, data, llen, comamd_len);
        break;
    default:
        sendack(remoteid, localid);
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    return comamd_len;
}
int handle_ccu_switch_rel(uint8_t *cmd_body, int len)
{
    uint8_t buff[64] = {0}, nlen, j = 0;
    for (int i = 0; i < len; i++)
    {
        buff[i] = cmd_body[i];
    }
    nlen = notify_message(internal_fifo_fd,buff, len);
    return nlen;
}
int handle_command_for_ccu(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    uint8_t comamd_len = 1;

    switch (data[0])
    {
    case CCU_SWITCH_REL:
        sendack(remoteid, localid);
        handle_ccu_switch_rel(data,llen);
        break;
    case CCU_SWITCH_ABS:
        sendack(remoteid, localid);
        break;
    case CCU_ANALOG_REL:
        comamd_len = 61;
        sendack(remoteid, localid);
        break;
    case CCU_DIAGNOSIS_INFORMATION:
        sendack(remoteid, localid);
        break;
    case 0x4d:
        sendack(remoteid, localid);
        break;
    case CCU_FORMAT_CONTROL:
        sendack(remoteid, localid);
        break;
    default:
        sendack(remoteid, localid);
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }

    return comamd_len + 1;
}
int handle_command_for_cnu(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    switch (data[0])
    {
    default:
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    sendack(remoteid, localid);

    return 0;
}
int handle_command_for_msu(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    switch (data[0])
    {
    default:
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    sendack(remoteid, localid);

    return 0;
}
int handle_command_for_vcs(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    switch (data[0])
    {
    default:
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    sendack(remoteid, localid);

    return 0;
}
int handle_command_for_rcp(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    switch (data[0])
    {
    default:
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    sendack(remoteid, localid);

    return 0;
}
int handle_command_for_ip(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    switch (data[0])
    {
    default:
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    sendack(remoteid, localid);

    return 0;
}
int handle_command_for_rm(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    switch (data[0])
    {
    default:
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    sendack(remoteid, localid);

    return 0;
}
int handle_command_for_other(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    switch (data[0])
    {
    default:
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    sendack(remoteid, localid);

    return 0;
}
int handle_command_for_reserved(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    switch (data[0])
    {
    default:
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    sendack(remoteid, localid);

    return 0;
}
int handle_command_for_local(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    switch (data[0])
    {
    default:
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    sendack(remoteid, localid);

    return 0;
}
int handle_command_for_special(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    switch (data[0])
    {
    default:
        printf("--------------------------------type:%02x\n", data[0]);
        break;
    }
    sendack(remoteid, localid);

    return 0;
}
int handle_command(uint8_t remoteid, uint8_t localid, uint8_t *data, int llen)
{
    int offset = 0;
    uint8_t group_command = data[0];
    printf("......................command_type:%02x\n", group_command);

    if (group_command >= 0x00 && group_command <= 0x1f)
    {
        offset = handle_general_purpose_commands(remoteid, localid, data, llen);
    }
    else if (group_command >= 0x20 && group_command <= 0x3f)
    {
        offset = handle_command_for_chu(remoteid, localid, data, llen);
    }
    else if (group_command >= 0x40 && group_command <= 0x5f)
    {
        offset = handle_command_for_ccu(remoteid, localid, data, llen);
    }
    else if (group_command >= 0x60 && group_command <= 0x6f)
    {
        offset = handle_command_for_cnu(remoteid, localid, data, llen);
    }
    else if (group_command >= 0x70 && group_command <= 0x7f)
    {
        offset = handle_command_for_msu(remoteid, localid, data, llen);
    }
    else if (group_command >= 0x80 && group_command <= 0x8f)
    {
        offset = handle_command_for_vcs(remoteid, localid, data, llen);
    }
    else if (group_command >= 0x90 && group_command <= 0x9f)
    {
        offset = handle_command_for_rcp(remoteid, localid, data, llen);
    }
    else if (group_command >= 0xa0 && group_command <= 0xaf)
    {
        offset = handle_command_for_ip(remoteid, localid, data, llen);
    }
    else if (group_command >= 0xb0 && group_command <= 0xbf)
    {
        offset = handle_command_for_rm(remoteid, localid, data, llen);
    }
    else if (group_command >= 0xc0 && group_command <= 0xd0)
    {
        offset = handle_command_for_other(remoteid, localid, data, llen);
    }
    else if (group_command >= 0xd1 && group_command <= 0xef)
    {
        offset = handle_command_for_reserved(remoteid, localid, data, llen);
    }
    else if (group_command >= 0xf0 && group_command <= 0xfd)
    {
        offset = handle_command_for_local(remoteid, localid, data, llen);
    }
    else if (group_command >= 0xfe && group_command <= 0xff)
    {
        offset = handle_command_for_special(remoteid, localid, data, llen);
    }
    return offset;
}